#! /usr/bin/env janet

(import shlib)
(import sh)

(sh/init)

(var *sysrc-file* "/etc/janetsh.rc")
(var *rc-file* (string (os/getenv "HOME") "/.janetsh.rc"))
(var *hist-file* nil)
(var *script* nil)
(var *parens* false)
(var *handleopts* true)


(defn- help-handler
  [&]
  (print "usage: janetsh [options] [script]")
  (print
  `Options are:
  -h | --help : Show this help and exit.
  -norc : Don't load the default rc file.
  -rc path : Use an alternative rc file.
  -nosysrc : Don't load the system rc file.
  -sysrc path : Use an alternative system rc file.
  -parens : Don't add implicit parens to interactive terminal.
  -- : Stop handling options`)
  (os/exit 1)
  1)

# Option handlers
(def handlers :private
  {"-h"     help-handler
   "--help" help-handler
   "--"     (fn [&] (set *handleopts* false) 1)
   "-norc"  (fn [&] (set *rc-file* nil) 1)
   "-rc"    (fn [i &] (set *rc-file* (get process/args (+ i 1))) 2)
   "-nosysrc"  (fn [&] (set *sysrc-file* nil) 1)
   "-sysrc"    (fn [i &] (set *sysrc-file* (get process/args (+ i 1))) 2)
   "-parens" (fn [&] (set *parens* true) 1)})

(defn- dohandler [n i &]
  (def h (get handlers n))
  (if h (h i) (do (print "unknown option -" n) ((get handlers "-h")))))

# Process arguments
(var i 2)
(def lenargs (length process/args))
(while (< i lenargs)
  (def arg (get process/args i))
  (if (and *handleopts* (= "-" (string/slice arg 0 1)))
    (+= i (dohandler arg i))
    (if *script*
      (error "only one script argument is allowed.")
      (do (set *script* arg) (+= i 1)))))

(var *get-prompt*
  (fn *get-prompt* [p]
    (string (os/cwd) " " (parser/state p) "$ ")))

# Use a var so users can redefine this in rc file.
(var *get-completion*
  (fn *get-completion*
    [line add-completion]
    (var expand-idx 0)
    (def rline (string/reverse line))
    (when-let [idx (string/find " " rline)]
      (set expand-idx (- (length line) idx)))
    (var to-expand (string (string/slice line expand-idx)))
    (var single-expansion (sh/expand to-expand))
    (when (= (length single-expansion) 1)
      (let [pattern (string (first single-expansion) "*")
            multi-expansion (sh/expand pattern)]
        (each choice multi-expansion
          (when (not= choice pattern)
            (let [choice-suffix (string/slice choice (length (first single-expansion)))
                  completion (string line choice-suffix)]
              (add-completion completion))))))))

(defn- getline [prompt buf]
  (defn get-completion [line lc]
    (*get-completion* line (partial shlib/ln/add-completion lc)))
  (when-let [ln (shlib/ln/get-line prompt get-completion)]
    (when (not= (length ln) 0)
      (shlib/ln/history-add ln))
    (buffer/push-string buf ln "\n")
    buf))

(defn- want-implicit-parens [buf p]
  (and (not *parens*) (> (length buf) 1) (empty? (parser/state p)) (not= (buf 0) 40)))

(defn- getchunk [buf p]
  (sh/update-all-jobs-status)
  (when (getline (*get-prompt* p) buf)
    (when (want-implicit-parens buf p)
      (let [line (string buf)]
        (buffer/clear buf)
        (buffer/format buf "(sh/$? %s)\n" line)))
    buf))

(setdyn :pretty-format "%.40p")

# convenience bindings for our user-env.
(def $?? :macro sh/$??)
(def $$_ :macro sh/$$_)
(def $$  :macro sh/$$)
(def $?  :macro sh/$?)
(def $   :macro sh/$)
(def clear sh/clear)
(def do-lines sh/do-lines)
(def out-lines sh/out-lines)

(def- user-env (fiber/getenv (fiber/current)))

(var *janetsh-repl*
  (fn *janetsh-repl*
    []
    (var hist-file nil)
    # Shell expand the hist file for the user.
    (when *hist-file*
      (set hist-file (first (sh/expand *hist-file*))))

    (when hist-file
      (shlib/ln/history-set-max-len 1024)
      (shlib/ln/set-multiline true)
      (try
        (shlib/ln/history-load hist-file)
        ([e] nil)))
    
    (repl getchunk nil user-env)
    
    (when hist-file
      (shlib/ln/history-save hist-file))))

(defn- run-interactive
  []
  # Load user rc file before
  (when *rc-file*
    (when (os/stat *rc-file*)
      # FIXME: filename in errors.
      (eval-string (slurp *rc-file*))))
  (*janetsh-repl*))

(defn- run-func
  []
  (when *sysrc-file*
    (when (os/stat *sysrc-file*)
      # FIXME: filename in errors.
      (eval-string (slurp *sysrc-file*))))
    
    (if *script*
      (import* *script* :prefix "" :exit true)
      (run-interactive)))

(def- user-fiber (fiber/new run-func :e))
(fiber/setenv user-fiber user-env)
(def- fiber-result (resume user-fiber))

# Prune jobs so less stale stuff is
# handled by our cleanup handlers.
(sh/prune-complete-jobs)

(if (= (fiber/status user-fiber) :error)
 (do
   (debug/stacktrace user-fiber fiber-result)
   (os/exit 1))
 (os/exit 0))
